package tinybase.rm;

import java.nio.ByteBuffer;
import java.util.Arrays;

import tinybase.basic.BytePointer;
import tinybase.basic.RC;
import tinybase.pf.*;

public class RM_Manager {
	public static final int RM_PAGE_LIST_END = -1;
	public static final int RM_HEADER_PAGE_NUM = 0;
	// je choisi 5 arbitraire,en fait je sais pas il dois etre combien
	public static final int RM_FILE_HDR_SIZE = (int) 20;

	private PF_Manager pPfm;

	public RM_Manager() {
		pPfm = new PF_Manager();
	}

	public RM_Manager(PF_Manager pfm) {
		this.pPfm= pfm;
	}

	public RC createFile(String fileName, int recordSize) {
		PF_FileHandle pfFileHandle = new PF_FileHandle();
		PF_PageHandle pageHandle = new PF_PageHandle();
		BytePointer refArray = new BytePointer();
		PageNum refPageNum = new PageNum();
		RM_FileHdr rmFileHdr = new RM_FileHdr();
		RC rc;

		// Sanity Check: recordSize should not be too large (or small)
		// Note that PF_Manager::CreateFile() will take care of fileName
		if (recordSize >= tinybase.pf.Pf.PF_PAGE_SIZE - RM_FILE_HDR_SIZE
				|| recordSize < 1)
			return (RC.RM_INVALIDRECSIZE);

		// Call PF_Manager::CreateFile()
		if ((rc = pPfm.createFile(fileName)) != RC.SUCCESS)
			return (rc);

		// Call PF_Manager::OpenFile()
		if ((rc = pPfm.openFile(fileName, pfFileHandle)) != RC.SUCCESS)
			return (rc);

		// Allocate the header page (pageNum must be 0)
		if ((rc = pfFileHandle.allocatePage(pageHandle)) != RC.SUCCESS)
			return (rc);

		// Get a pointer where header information will be written
		if (((rc = pageHandle.getData(refArray)) != RC.SUCCESS)
				|| (rc = pageHandle.getPageNum(refPageNum)) != RC.SUCCESS)
			return (rc);
		
//		ByteBuffer.wrap(refArray.getArray()).putInt(PF_PageHandle.DATASTART_OFFSET, refPageNum.getPageNum());

		// Write the file header (to the buffer pool)
		ByteBuffer fileHdrBuf = ByteBuffer.wrap(refArray.getArray());
		fileHdrBuf.putInt(PF_PageHandle.DATASTART_OFFSET + 0,
				RM_Internal.RM_PAGE_LIST_END);
		fileHdrBuf.putInt(PF_PageHandle.DATASTART_OFFSET + 4,
				recordSize);
		int numRecordsPerPage = (int)((tinybase.pf.Pf.PF_PAGE_SIZE - RM_PageHdr.RM_PageHdr_SIZE -1) / (recordSize + 1.0/8));
		if (recordSize * (numRecordsPerPage + 1) + numRecordsPerPage / 8
				<= tinybase.pf.Pf.PF_PAGE_SIZE - RM_PageHdr.RM_PageHdr_SIZE - 1) {
			numRecordsPerPage++;
		}
		fileHdrBuf.putInt(PF_PageHandle.DATASTART_OFFSET + 8, numRecordsPerPage);
		fileHdrBuf.putInt(PF_PageHandle.DATASTART_OFFSET + 12, RM_PageHdr.RM_PageHdr_SIZE + (numRecordsPerPage + 7) / 8);
		fileHdrBuf.putInt(PF_PageHandle.DATASTART_OFFSET + 16, 0);
//		rmFileHdr.setFirstFree(RM_PAGE_LIST_END);
//		rmFileHdr.setRecordSize(recordSize);

		// Mark the header page as dirty
		if ((rc = pfFileHandle.markDirty(RM_HEADER_PAGE_NUM)) != RC.SUCCESS)
			return rc;

		// Unpin the header page
		if ((rc = pfFileHandle.unpinPage(RM_HEADER_PAGE_NUM)) != RC.SUCCESS)
			return rc;

		// Call PF_Manager::CloseFile()
		if ((rc = pPfm.closeFile(pfFileHandle)) != RC.SUCCESS)
			return rc;

		return RC.SUCCESS;
	}


	//
	// DestroyFile
	//
	// Desc: Delete a RM file named fileName (fileName must exist and not be open)
	// In:   fileName - name of file to delete
	// Ret:  PF return code
	//
	public RC destroyFile(String fileName) {
		RC rc;
		if ((rc = pPfm.destroyFile(fileName)) != RC.SUCCESS)
			return (rc);
		return RC.SUCCESS;
	}


	//
	// OpenFile
	//
	// Desc: Open the paged file whose name is "fileName".
	//	       Copy the file header information into a private variable in
	//	       the file handle so that we can unpin the header page immediately
	//	       and later find out details without reading the header page
	// In:   fileName - name of file to open
	// Out:  fileHandle - refer to the open file
	// Ret:  PF return code
	//
	public RC openFile(String fileName, RM_FileHandle rmFileHandle){
		RC rc;
		PF_PageHandle pfPageHandle = new PF_PageHandle();
		BytePointer refpData = new BytePointer();
		byte[] pData = null;
//System.exit(1);
		// Call PF_Manager::OpenFile()
		if ((rc = pPfm.openFile(fileName, rmFileHandle.pfFileHandle)) != RC.SUCCESS)
			// Test: non-existing fileName, opened fileHandle
			return rc;

		// Get the header page
		if ((rc = rmFileHandle.pfFileHandle.getFirstPage(pfPageHandle)) != RC.SUCCESS)
			// Test: invalid file
			return rc;

		// Get a pointer where header information resides
		if ((rc = pfPageHandle.getData(refpData)) != RC.SUCCESS)
			// Should not happen
			return rc;

		pData = refpData.getArray();
		// Read the file header (from the buffer pool to RM_FileHandle)
		//ByteBuffer.wrap(pData.getArray());
		rmFileHandle.getFileHdr().copyFromBytes(pData, PF_PageHandle.DATASTART_OFFSET);

		// Unpin the header page
		if ((rc = rmFileHandle.pfFileHandle.unpinPage(RM_HEADER_PAGE_NUM)) != RC.SUCCESS)
			// Should not happen
			return rc;
		// TODO: cannot guarantee the validity of file header at this time

		// Set file header to be not changed
		rmFileHandle.setbHdrChanged(true);       /////////  Narcisse to be verified 

		return RC.SUCCESS;
	}


	//
	// CloseFile
	// 
	// Desc: Close file associated with fileHandle
	//	       Write back the file header (if there was any changes)
	// In:   fileHandle - handle of file to close
	// Out:  fileHandle - no longer refers to an open file
	// Ret:  RM return code
	//
	public RC closeFile(RM_FileHandle rmFileHandle) {
		RC rc;

		// Write back the file header if any changes made to the header
		// while the file is open
		if (rmFileHandle.bHdrChanged == true) {
			PF_PageHandle pfPageHandle = new PF_PageHandle();
			BytePointer refArray = new BytePointer();
			PageNum refPageNum = new PageNum();

			// Get the header page
			if ((rc = rmFileHandle.pfFileHandle.getFirstPage(pfPageHandle)) != RC.SUCCESS)
				// Test: unopened(closed) fileHandle, invalid file
				return rc;

			// Write the file header (to the buffer pool)
			if (((rc = pfPageHandle.getData(refArray)) != RC.SUCCESS)
					|| (rc = pfPageHandle.getPageNum(refPageNum)) != RC.SUCCESS)
				return (rc);
			
			System.arraycopy(rmFileHandle.fileHdr.toBytes(), 0, refArray.getArray(), 
					PF_PageHandle.DATASTART_OFFSET, RM_Internal.RM_FILEHDR_SIZE);
			
			
			// Mark the header page as dirty
			if ((rc = rmFileHandle.pfFileHandle.markDirty(RM_HEADER_PAGE_NUM)) != RC.SUCCESS)
				// Should not happen
				return rc;

			// Unpin the header page
			if ((rc = rmFileHandle.pfFileHandle.unpinPage(RM_HEADER_PAGE_NUM)) != RC.SUCCESS)
				// Should not happen
				return rc;

			// Set file header to be not changed
			rmFileHandle.setbHdrChanged(false);      //////// Narcisse to be verified
		}

		// Call PF_Manager::CloseFile()
		if ((rc = pPfm.closeFile(rmFileHandle.pfFileHandle)) != RC.SUCCESS)
			// Test: unopened(closed) fileHandle
			return rc;

		// Reset member variables
		rmFileHandle.fileHdr.copyFromBytes(new byte[RM_Internal.RM_FILEHDR_SIZE], 0);
		rmFileHandle.fileHdr.setFirstFree(RM_PAGE_LIST_END);

		return RC.SUCCESS;
	}

}
